﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media;

namespace System.Controls
{
  public class Hsl
  {
    private float fAlpha;
    private float fBrightness;
    private float fHue;
    private float fSaturation;

    public Hsl() {}

    public Hsl( Color source )
    {
      var color = System.Drawing.Color.FromArgb( source.A, source.R, source.G, source.B );

      Alpha = source.A/255f;
      Brightness = color.GetBrightness();
      Hue = color.GetHue();
      Saturation = color.GetSaturation();
    }

    public float Alpha
    {
      get { return fAlpha; }
      set
      {
        if ( value < 0f || value > 1f )
          throw new ArgumentOutOfRangeException( "value", "Alpha must be within a range of 0 - 1." );

        fAlpha = value;
      }
    }

    public float Brightness
    {
      get { return fBrightness; }
      set
      {
        if ( 0f > value || 1f < value )
          throw new ArgumentOutOfRangeException( "value", "Value must be within a range of 0 - 1." );

        fBrightness = value;
      }
    }

    public float Hue
    {
      get { return fHue; }
      set
      {
        if ( fHue < 0f || fHue > 360f )
          throw new ArgumentOutOfRangeException( "value", "Hue must be within a range of 0 - 360." );

        fHue = value;
      }
    }

    public float Saturation
    {
      get { return fSaturation; }
      set
      {
        if ( 0f > value || 1f < value )
          throw new ArgumentOutOfRangeException( "value", "Saturation must be within a range of 0 - 1." );

        fSaturation = value;
      }
    }

    public Color Color
    {
      get
      {
        if ( Math.Abs( Saturation ) < 1 )
        {
          return Color.FromArgb( Convert.ToByte( Alpha*255 ),
            Convert.ToByte( Brightness*255 ),
            Convert.ToByte( Brightness*255 ),
            Convert.ToByte( Brightness*255 ) );
        }

        float max, mid, min;

        if ( 0.5 < Brightness )
        {
          max = Brightness - ( Brightness*Saturation ) + Saturation;
          min = Brightness + ( Brightness*Saturation ) - Saturation;
        }
        else
        {
          max = Brightness + ( Brightness*Saturation );
          min = Brightness - ( Brightness*Saturation );
        }

        var iSextant = (int)Math.Floor( Hue/60f );
        if ( 300f <= Hue )
          Hue -= 360f;

        Hue /= 60f;
        Hue -= 2f*(float)Math.Floor( ( ( iSextant + 1f )%6f )/2f );
        if ( 0 == iSextant%2 )
          mid = ( Hue*( max - min ) ) + min;
        else
          mid = min - ( Hue*( max - min ) );

        var iMax = Convert.ToByte( max*255 );
        var iMid = Convert.ToByte( mid*255 );
        var iMin = Convert.ToByte( min*255 );

        switch ( iSextant )
        {
        case 1:
          return Color.FromArgb( Convert.ToByte( Alpha*255 ), iMid, iMax, iMin );

        case 2:
          return Color.FromArgb( Convert.ToByte( Alpha*255 ), iMin, iMax, iMid );

        case 3:
          return Color.FromArgb( Convert.ToByte( Alpha*255 ), iMin, iMid, iMax );

        case 4:
          return Color.FromArgb( Convert.ToByte( Alpha*255 ), iMid, iMin, iMax );

        case 5:
          return Color.FromArgb( Convert.ToByte( Alpha*255 ), iMax, iMin, iMid );
        }

        return Color.FromArgb( Convert.ToByte( Alpha*255 ), iMax, iMid, iMin );
      }
    }

    public void Adjust( float hue, float saturation, float brightness )
    {
      Hue += hue/360;
      if ( Hue > 360 )
        Hue = 360;
      else if ( Hue < 0 )
        Hue = 0;

      Saturation += saturation/100;
      if ( Saturation > 1 )
        Saturation = 1;
      else if ( Saturation < 0 )
        Saturation = 0;

      Brightness += brightness/100;
      if ( Brightness > 1 )
        Brightness = 1;
      else if ( Brightness < 0 )
        Brightness = 0;
    }
  }
}
